﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using SteamFluentAPI.Infrastructure.Base;

namespace SteamFluentAPI.Infrastructure.MetaData
{
    public class SteamMetaDataCollection
    {
        private List<SteamInterfaceMetaData> _interfaces;
        /// <summary>
        /// Returns a list of steam interface meta data.
        /// </summary>
        public List<SteamInterfaceMetaData> Interfaces { get { return _interfaces; } }
        
        private List<SteamMethodMetaData> _methods;
        /// <summary>
        /// Returns a list of steam method meta data.
        /// </summary>
        public List<SteamMethodMetaData> Methods { get { return _methods; } }

        private List<SteamParameterMetaData> _parameters;
        /// <summary>
        /// Returns a list of steam parameter meta data.
        /// </summary>
        public List<SteamParameterMetaData> Parameters { get { return _parameters; } }

        /// <summary>
        /// Initializes and constructs a collection of meta data including relations.
        /// </summary>
        public SteamMetaDataCollection()
        {
            _interfaces = new List<SteamInterfaceMetaData>();
            _methods = new List<SteamMethodMetaData>();
            _parameters = new List<SteamParameterMetaData>();

            foreach (PropertyInfo interfaceInfo in typeof(SteamInterfaceCollection).GetProperties())
            {
                Type interfaceType = interfaceInfo.PropertyType;
                SteamInterfaceAttribute interfaceAtt = interfaceInfo.GetCustomAttribute<SteamInterfaceAttribute>();

                if (interfaceAtt != null)
                {
                    SteamInterfaceMetaData face = new SteamInterfaceMetaData();
                    face.Name = interfaceInfo.Name;
                    face.ActualInterfaceName = string.IsNullOrEmpty(interfaceAtt.InterfaceName) ? face.Name : interfaceAtt.InterfaceName;
                    face.RequiresGameType = interfaceAtt.RequiresGameType;
                    face.SteamGameType = interfaceAtt.SteamGameType;
                    face.IsImplemented = interfaceInfo.IsImplemented();

                    foreach (PropertyInfo methodInfo in interfaceType.GetProperties())
                    {
                        Type methodType = methodInfo.PropertyType;
                        SteamMethodAttribute methodAtt = methodInfo.GetCustomAttribute<SteamMethodAttribute>();

                        if (methodAtt != null)
                        {
                            SteamMethodMetaData method = new SteamMethodMetaData();
                            method.Interface = face;
                            method.Name = methodInfo.Name;
                            method.ActualMethodName = string.IsNullOrEmpty(methodAtt.MethodName) ? method.Name : methodAtt.MethodName;
                            method.VersionCode = string.IsNullOrEmpty(methodAtt.VersionCode) ? "N/A" : methodAtt.VersionCode;
                            method.IsImplemented = methodInfo.IsImplemented();

                            foreach (MethodInfo parameterInfo in methodType.GetMethods())
                            {
                                if (!parameterInfo.Name.Equals("GetType") ||
                                    !parameterInfo.Name.Equals("GetHashCode") ||
                                    !parameterInfo.Name.Equals("Equals") ||
                                    !parameterInfo.Name.Equals("ToString") ||
                                    !parameterInfo.Name.Equals("GetParameters"))
                                {
                                    SteamParameterMetaData parameter = new SteamParameterMetaData();
                                    parameter.Method = method;
                                    parameter.Name = parameterInfo.Name;
                                    parameter.IsImplemented = true;
                                    parameter.IsRequired = (parameterInfo.GetCustomAttribute<RequiredParameterAttribute>() != null);
                                    method.Parameters.Add(parameter);

                                    if (!_parameters.Contains(parameter))
                                    {
                                        _parameters.Add(parameter);
                                    }
                                }
                            }

                            face.Methods.Add(method);
                            _methods.Add(method);
                        }
                    }

                    _interfaces.Add(face);
                }
            }
        }
        /// <summary>
        /// Gets a list of steam interface names.
        /// </summary>
        /// <returns></returns>
        public List<string> GetInterfaceNames()
        {
            return _interfaces.Select(x => x.Name).ToList();
        }
        /// <summary>
        /// Gets a list of steam method names.
        /// </summary>
        /// <returns></returns>
        public List<string> GetMethodNames()
        {
            return _methods.Select(x => x.Name).ToList();
        }
        /// <summary>
        /// Gets a list of steam method names which belong to the specified interface.
        /// </summary>
        /// <param name="interfaceName"></param>
        /// <returns></returns>
        public List<string> GetMethodNames(string interfaceName)
        {
            SteamInterfaceMetaData face = _interfaces.SingleOrDefault(x => x.Name.ToLower() == interfaceName.ToLower());

            if (face != null)
            {
                return face.Methods.Select(x => x.Name).ToList();
            }
            return null;
        }
        /// <summary>
        /// Gets a list of steam parameters.
        /// </summary>
        /// <returns></returns>
        public List<string> GetParameters()
        {
            return _parameters.Select(x => x.Name).ToList();
        }
        /// <summary>
        /// Gets a list of steam parameters which belong to the specified method.
        /// </summary>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public List<string> GetParameters(string methodName)
        {
            SteamMethodMetaData method = _methods.SingleOrDefault(x => x.Name.ToLower() == methodName.ToLower());

            if (method != null)
            {
                return method.Parameters.Select(x => x.Name).ToList();
            }
            return null;
        }

        #region Hiding Methods

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override Boolean Equals(Object obj)
        {
            return base.Equals(obj);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override Int32 GetHashCode()
        {
            return base.GetHashCode();
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override String ToString()
        {
            return base.ToString();
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public new Type GetType()
        {
            return base.GetType();
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public static new bool ReferenceEquals(object objA, object objB)
        {
            return SteamAPI.ReferenceEquals(objA, objB);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public static new bool Equals(object objA, object objB)
        {
            return SteamAPI.Equals(objA, objB);
        }

        #endregion
    }
}
